Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

feat: Error Details Improvements - GRPC #1634

Merged
merged 15 commits into from
Apr 4, 2022
Merged

Conversation

blakeli0
Copy link
Contributor

@blakeli0 blakeli0 commented Mar 15, 2022

This PR is for issue #1635
Key design notes:

  1. All ApiException created for GRPC clients will go through GrpcApiExceptionFactory and the cause of ApiException already has all the info we need, hence the majority of the logic is in GrpcApiExceptionFactory.
  2. Promoted all the fields of ErrorInfo to top level of ApiException, accessible through getters directly.
  3. Encapsulated all the raw error messages from server to ErrorDetails. The unpacked error messages are accessible through getters.
  4. If error messages from server are corrupted and unable to unpack, a ProtocolBufferParsingException will be thrown.

gax/src/main/java/com/google/api/gax/rpc/ApiException.java Outdated Show resolved Hide resolved

public CancelledException(
Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) {
super(cause, statusCode, retryable, errorDetails);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subjective: For this an all similar changes, can you make them such that each of the overloaded constructors call this() (except the last one, which does the job). This is an idiomatic way of doing it and is often easier to maintain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean making the first constructor call this(cause, statusCode, retryable, null)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. It is a typical approach: to have some sort of a "master" consturctor and make other constructors simply call the big one. The master constructor can even be private, if we don't want to expose it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this is ideal if possible, but I think it depends on whether super(field1, field2) has the same behavior as this(field1, field2, null), sometimes they don't. For example, super(cause) and this(null, cause) in RuntimeException have different behaviors, one will populate message from cause, the other will set it to null. That being said, since this is a side refactoring, maybe we can merge the main logic of the code first and come back to see if we can improve it.

@blakeli0 blakeli0 marked this pull request as ready for review March 21, 2022 01:37
@blakeli0 blakeli0 requested review from a team as code owners March 21, 2022 01:37
@blakeli0 blakeli0 linked an issue Mar 21, 2022 that may be closed by this pull request
@blakeli0 blakeli0 changed the title Add ErrorDetails to ApiException. feat: Error Details Improvements - GRPC Mar 21, 2022
} catch (InvalidProtocolBufferException e) {
// If unpacking one of the error detail fails, it should not block unpacking other error
// details so that end users can still get some info back. Hence, we don't need to do
// anything.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just swallow this, if details are all corrupt we keep continuing and then create an empty error details and have no insight into what's going on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in this case, an empty ErrorDetails will be created. I think an empty ErrorDetails is a legitimate case, it could happen either when server doesn't send anything or we are unable to unpack the message server sends.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we in his case just store the raw Any (not unpacked) detail object in ErrorDetails? Doing empyt error details will just loose information for users and make the error even more confusing to them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing raw Any would not be useful to users in my opinion, they don't know how to unpack it and even if they do they can not unpack it either due to exceptions. It might be more confusing to them because they don't know what it is and don't know what to do with it.
What I can do though is to add more java docs to this class or the getter to describe what could be null in what scenarios.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree they could not unpack it. And just the fact that there is something encoded in any way gives a developer enough clue that there is a speciifc error and they should be able to unpack it sooner or later. It is better than hiding info completely. Another approach may be instead of storing any, convert it to string and store as a string.

Copy link
Contributor

@summer-ji-eng summer-ji-eng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @blakeli0 for your hard work. Java seems more complicated than other languages.

LGTM on actionable error logic. Please wait for the Java expertise review on language idiom or syntax.

Copy link
Contributor

@vam-google vam-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, but I'm concerned with the extra dependency in gax module. Gax is a very core dependency of all google cloud libraries, adding something there is a big deal. Looks like up untill this point, gax was not aware of protobuf, and maybe we should keep it this way (i.e. in ErrorDetails do not have those error details classes directly. Maybe have some sort of GrpcErrorDetails in gax-grpc implementing a more generic ErrorDetails from gax.


private void addErrorDetail(ErrorDetails.Builder errorDetailsBuilder, Any detail) {
try {
if (detail.is(ErrorInfo.class)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, is it possible to convert this to a switch statement somehow? For example using class names as strings, as switch accepts strings as the switched value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it some thoughts as well but couldn't find a good way to use switch statements, I don't think using class names as String would work either. Because we are not checking a variable, we are checking the result of method call, and the input of the method call is a variable. We can only get the type of Any through this method call instead of an attribute, otherwise we could've check it easily.
Let me know if you have some other good ideas.

} catch (InvalidProtocolBufferException e) {
// If unpacking one of the error detail fails, it should not block unpacking other error
// details so that end users can still get some info back. Hence, we don't need to do
// anything.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we in his case just store the raw Any (not unpacked) detail object in ErrorDetails? Doing empyt error details will just loose information for users and make the error even more confusing to them.

gax/BUILD.bazel Show resolved Hide resolved
gax/src/main/java/com/google/api/gax/rpc/ApiException.java Outdated Show resolved Hide resolved

public CancelledException(
Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) {
super(cause, statusCode, retryable, errorDetails);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. It is a typical approach: to have some sort of a "master" consturctor and make other constructors simply call the big one. The master constructor can even be private, if we don't want to expose it.

Copy link
Contributor

@vam-google vam-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned, in my previous comment overall it looks good, but we need to figure out the dependency problem first and placement of the new classes. Putting "request changes" here to prevent accidental push of this PR before we figure out the deps part.

Copy link
Contributor

@vam-google vam-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but please address the remaining comments first.

Looks like I have to accept the new dependencies thing, but please check the comments (and i have a few questions there).

For the new methods in ApiExcepton - please strongly consider removing them. The cross-language desing usually never dictates such implementation-specific things. @summer-ji-eng please advaice.

} catch (InvalidProtocolBufferException e) {
// If unpacking one of the error detail fails, it should not block unpacking other error
// details so that end users can still get some info back. Hence, we don't need to do
// anything.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree they could not unpack it. And just the fact that there is something encoded in any way gives a developer enough clue that there is a speciifc error and they should be able to unpack it sooner or later. It is better than hiding info completely. Another approach may be instead of storing any, convert it to string and store as a string.

@@ -5,7 +5,8 @@ project.version = "2.12.3-SNAPSHOT" // {x-version-update:gax:current}

dependencies {
api(libraries['maven.com_google_api_api_common'],
libraries['maven.com_google_auth_google_auth_library_credentials'],
libraries['maven.com_google_api_grpc_proto_google_common_protos'],
libraries['maven.com_google_auth_google_auth_library_credentials'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this one needed?

Copy link
Contributor Author

@blakeli0 blakeli0 Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean com_google_auth_google_auth_library_credentials? I think It's a formatting/git issue, I added com_google_api_grpc_proto_google_common_protos but somehow git thinks I removed one and added two.

gax/BUILD.bazel Show resolved Hide resolved
Copy link
Member

@meltsufin meltsufin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just one minor comment on adding a message to ProtocolBufferParsingException.

gax/src/main/java/com/google/api/gax/rpc/ErrorDetails.java Outdated Show resolved Hide resolved
@sonarqubecloud
Copy link

sonarqubecloud bot commented Apr 1, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

98.6% 98.6% Coverage
0.0% 0.0% Duplication

@sonarqubecloud
Copy link

sonarqubecloud bot commented Apr 4, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

98.6% 98.6% Coverage
0.0% 0.0% Duplication

@blakeli0 blakeli0 merged commit 00c3b9d into main Apr 4, 2022
@blakeli0 blakeli0 deleted the actionable-error-grpc branch April 4, 2022 15:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error Details Improvements - GRPC
5 participants